package net.cyue.nest.nestjava.core;

import jakarta.validation.Valid;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import net.cyue.nest.nestjava.common.annotation.core.Controller;
import net.cyue.nest.nestjava.common.annotation.core.Inject;
import net.cyue.nest.nestjava.common.annotation.core.UseInterceptors;
import net.cyue.nest.nestjava.common.annotation.http.*;
import net.cyue.nest.nestjava.common.data.http.CORSOption;
import net.cyue.nest.nestjava.common.enums.HTTPStatus;
import net.cyue.nest.nestjava.common.interfaces.Callback;
import net.cyue.nest.nestjava.common.interfaces.INestApplication;
import net.cyue.nest.nestjava.common.interfaces.feature.INestInterceptor;
import net.cyue.nest.nestjava.common.interfaces.http.IHTTPRequest;
import net.cyue.nest.nestjava.common.interfaces.http.IHTTPResponse;
import net.cyue.nest.nestjava.common.interfaces.http.IHTTPServer;
import net.cyue.nest.nestjava.common.interfaces.http.IRequestHandler;
import net.cyue.nest.nestjava.common.util.JSONUtil;
import net.cyue.nest.nestjava.common.util.ReflectUtil;
import net.cyue.nest.nestjava.core.adapter.AbstractHTTPAdapter;
import net.cyue.nest.nestjava.core.injector.Injector;
import net.cyue.nest.nestjava.core.injector.InstanceWrapper;
import net.cyue.nest.nestjava.core.injector.NestContainer;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class NestApplication<TServerAdapter extends AbstractHTTPAdapter<?>>
  extends NestApplicationContext
  implements INestApplication<TServerAdapter>
{
  // body 参数验证器
  private static final ValidatorFactory VALIDATOR_FACTORY;
  private static final Validator VALIDATOR; // body validator
  static {
    VALIDATOR_FACTORY = Validation.buildDefaultValidatorFactory();
    VALIDATOR = VALIDATOR_FACTORY.getValidator();
  }

  private final TServerAdapter httpAdapter;
  private String globalPrefix = "";
  private INestInterceptor[] globalInterceptors = new INestInterceptor[0];

  public NestApplication(
    Class<?> appModuleClass,
    NestContainer nestContainer,
    TServerAdapter httpAdapter
  ) {
    super(appModuleClass, nestContainer);
    this.httpAdapter = httpAdapter;
  }

  private String dealHandlerPath(String... paths) {
    if (paths == null) {
      return null;
    }
    if (paths.length == 0) {
      return null;
    }

    String[] strings = new String[paths.length];
    for (int i = 0; i < paths.length; i++) {
      if (paths[i].isEmpty()) {
        continue;
      }
      strings[i] = paths[i];
      if (strings[i].equals("/")) {
        strings[i] = "";
      } else if (strings[i].startsWith("/") && strings[i].endsWith("/")) {
        strings[i] = strings[i].substring(1, strings[i].length() - 1);
      } else if (strings[i].startsWith("/")) {
        strings[i] = strings[i].substring(1);
      } else if (strings[i].endsWith("/")) {
        strings[i] = strings[i].substring(0, strings[i].length() - 1);
      }
    }

    StringBuilder builder = new StringBuilder();
    for (String s : strings) {
      if (s == null || s.isEmpty()) {
        continue;
      }
      builder.append("/");
      builder.append(s);
    }

    String finalPath = this.globalPrefix + builder;
    return finalPath.isEmpty() ? "/" : finalPath;
  }
  private String[] dealHandlerPathsWithPrefix(String prefix, String... paths) {
    if (paths == null || paths.length == 0) {
      return new String[]{ prefix };
    }
    if (prefix == null) {
      prefix = "";
    }
    String[] result = new String[paths.length];
    for (int i = 0; i < paths.length; i++) {
      result[i] = this.dealHandlerPath(prefix, paths[i]);
    }
    return result;
  }
  private boolean runInterceptor(
    IHTTPRequest request,
    IHTTPResponse response,
    INestInterceptor interceptor
  ) {
    try {
      Method interceptMethod = interceptor.getClass().getDeclaredMethod(
        "intercept",
        IHTTPRequest.class,
        IHTTPResponse.class
      );
      if (interceptMethod.getAnnotation(ResponseBody.class) != null) {
        response.putHeader("Content-Type", "application/json;charset=UTF-8");
      } else if (interceptMethod.getAnnotation(ResponseHTML.class) != null) {
        response.putHeader("Content-Type", "text/html;charset=UTF-8");
      }
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
    return interceptor.intercept(request, response);
  }

  private IRequestHandler createRequestHandler(
    InstanceWrapper controllerInstanceWrapper,
    Method m,
    INestInterceptor... interceptors
  ) {
    Object controllerInstance = controllerInstanceWrapper.getInstance();
    return (request, response) -> {
      // 默认 Content-Type
      response.putHeader("Content-Type", "text/plain;charset=UTF-8");

      // global 拦截器
      for (INestInterceptor i : this.globalInterceptors) {
        // false 则结束
        if (!this.runInterceptor(request, response, i)) {
          return;
        }
      }

      // 执行 拦截器方法
      for (INestInterceptor i : interceptors) {
        // false 则结束
        if (!this.runInterceptor(request, response, i)) {
          return;
        }
      }

      try {
        // 获取方法参数，并根据注解Request或Response填充参数值
        Parameter[] parameters = m.getParameters();
        Object[] objs = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++)
        {
          Inject inject = parameters[i].getAnnotation(Inject.class);
          if (inject != null) {
            Object instance = Injector.getInjectableInstance(inject);
            if (instance == null) {
              response.code(HTTPStatus.INTERNAL_SERVER_ERROR);
              response.end("500");
              throw new IllegalArgumentException("错误的 injectable 名称");
            }
            objs[i] = instance;
          }
          else if (parameters[i].getAnnotation(Request.class) != null) {
            objs[i] = request;
          } else if (parameters[i].getAnnotation(Response.class) != null) {
            objs[i] = response;
          } else if (parameters[i].getAnnotation(Body.class) != null) {
            // body 参数
            final var body = request.body();
            if (body == null) {
              continue;
            }

            Class<?> bodyClass = parameters[i].getType();
            objs[i] = ReflectUtil.createInstance(bodyClass); // 创建 body 参数实例

            final var bodyClassFields = bodyClass.getDeclaredFields();
            for (final var field : bodyClassFields) {
              field.setAccessible(true);
              final var fieldName = field.getName();
              final var fieldValue = body.get(fieldName);
              if (fieldValue == null) {
                continue;
              }
              ReflectUtil.setFieldValue(objs[i], field, fieldValue);
            }

            // valid 验证
            if (parameters[i].getAnnotation(Valid.class) == null) {
              continue;
            }
            final var validationResultSet = NestApplication.VALIDATOR.validate(objs[i]);
            if (!validationResultSet.isEmpty()) {
              final var r1 = validationResultSet.iterator().next();
              Map<String, Object> returnMap = new HashMap<>();
              returnMap.put("message", r1.getPropertyPath() + r1.getMessage());
              returnMap.put("time", new Date());

              response.code(HTTPStatus.BAD_REQUEST);
              response.putHeader("Content-Type", "application/json");
              response.end(JSONUtil.toJSONString(returnMap));
              return;
            }
          } else {
            objs[i] = null;
          }
        }

        // 调用控制器方法
        if (m.getAnnotation(ResponseBody.class) != null) {
          response.putHeader("Content-Type", "application/json;charset=UTF-8");
        } else if (m.getAnnotation(ResponseHTML.class) != null) {
          response.putHeader("Content-Type", "text/html;charset=UTF-8");
        }
        Object result = m.invoke(controllerInstance, objs);
        if (result == null) {
          return;
        }
        if (result instanceof String) {
          response.end((String) result);
        } else {
          response.send(result);
        }
      } catch (Exception e) {
        e.printStackTrace();
        response.code(HTTPStatus.INTERNAL_SERVER_ERROR);
        response.end("500");
      }
    };
  }
  private void initController(InstanceWrapper controllerInstanceWrapper) {
    final Object controllerInstance = controllerInstanceWrapper.getInstance();
    final var controllerAnnotation = controllerInstance.getClass().getAnnotation(Controller.class);
    if (controllerAnnotation == null) {
      return;
    }
    // 获取控制器实例的所有方法
    Method[] controllerMethods = controllerInstance.getClass().getMethods();
    for (final Method m : controllerMethods) {
      // 设置方法可访问
      m.setAccessible(true);

      // 获取方法上的注解，创建拦截器
      final var useInterceptorsAnnotation = m.getAnnotation(UseInterceptors.class);
      Class<?>[] interceptorTypes =
        useInterceptorsAnnotation != null
          ? useInterceptorsAnnotation.value()
          : new Class[0];
      INestInterceptor[] interceptors = new INestInterceptor[interceptorTypes.length];
      if (interceptorTypes.length != 0) {
        for (int i = 0; i < interceptorTypes.length; i++) {
          interceptors[i] = (INestInterceptor) ReflectUtil.createInstance(interceptorTypes[i]);
        }
      }

      // 创建 request handler
      IRequestHandler handler = this.createRequestHandler(controllerInstanceWrapper, m, interceptors);

      // 获取方法上的注解，创建路由
      final var requestMappingAnnotation = m.getAnnotation(RequestMapping.class);
      final var postMethodAnnotation = m.getAnnotation(PostMethod.class);
      final var getMethodAnnotation = m.getAnnotation(GetMethod.class);
      final var deleteMethodAnnotation = m.getAnnotation(DeleteMethod.class);
      final var PutMethodAnnotation = m.getAnnotation(PutMethod.class);
      final var patchMethodAnnotation = m.getAnnotation(PatchMethod.class);

      if (requestMappingAnnotation != null) {
        String[] paths = this.dealHandlerPathsWithPrefix(
          controllerAnnotation.value(),
          requestMappingAnnotation.value()
        );
        for (String p : paths) {
          this.httpAdapter.all(p, handler);
        }
        continue;
      }
      if (postMethodAnnotation != null) {
        String[] paths = this.dealHandlerPathsWithPrefix(
          controllerAnnotation.value(),
          postMethodAnnotation.value()
        );
        for (String p : paths) {
          this.httpAdapter.post(p, handler);
        }
      }
      if (getMethodAnnotation != null) {
        String[] paths = this.dealHandlerPathsWithPrefix(
          controllerAnnotation.value(),
          getMethodAnnotation.value()
        );
        for (String p : paths) {
          this.httpAdapter.get(p, handler);
        }
      }
      if (deleteMethodAnnotation != null) {
        String[] paths = this.dealHandlerPathsWithPrefix(
          controllerAnnotation.value(),
          deleteMethodAnnotation.value()
        );
        for (String p : paths) {
          this.httpAdapter.delete(p, handler);
        }
      }
      if (PutMethodAnnotation != null) {
        String[] paths = this.dealHandlerPathsWithPrefix(
          controllerAnnotation.value(),
          PutMethodAnnotation.value()
        );
        for (String p : paths) {
          this.httpAdapter.put(p, handler);
        }
      }
      if (patchMethodAnnotation != null) {
        String[] paths = this.dealHandlerPathsWithPrefix(
          controllerAnnotation.value(),
          patchMethodAnnotation.value()
        );
        for (String p : paths) {
          this.httpAdapter.patch(p, handler);
        }
      }
    }
  }
  /**
   * 初始化控制器，扫描并处理所有模块中的控制器方法。
   * 为此，首先获取所有模块的容器，然后遍历每个模块，获取其控制器实例。
   * 注册HTTP请求的处理程序。
   */
  private void initControllers() {
    // 获取当前Nest容器中的模块容器
    final var moduleContainer = this.getNestContainer().getModuleContainer();
    // 遍历模块容器中所有的模块
    final var modules = moduleContainer.values();
    for (final var module : modules) {
      // 获取模块中所有的控制器实例
      final var controllerInstanceWrappers = module.getControllerInstanceWrappers();
      // 遍历所有控制器
      for (final var controllerInstanceWrapper : controllerInstanceWrappers) {
        this.initController(controllerInstanceWrapper);
      }
    }
  }

  /**
   * A wrapper function around HTTP adapter method: `adapter.use()`.
   * Example `app.use(cors())`
   *
   * @param handlers handlers
   */
  @Override
  public INestApplication<TServerAdapter> use(IRequestHandler... handlers) {
    for (final var handler : handlers) {
      this.httpAdapter.use(handler);
    }
    return this;
  }

  @Override
  public void enableCoors(CORSOption option) {

  }

  @Override
  public void listen(int port) {
    this.listen(port, () -> {});
  }

  @Override
  public void listen(int port, Callback callback) {
    this.initControllers();
    System.out.println("监听 " + port + " 端口");
    if (callback != null) {
      callback.call();
    }
    this.httpAdapter.listen(port);
    if (callback != null) {
      callback.call();
    }
  }

  /**
   * Returns the url the application is listening at, based on OS and IP version. Returns as an IP value either in IPv6 or IPv4
   */
  @Override
  public String getURL() {
    return null;
  }

  @Override
  public INestApplication<TServerAdapter> setGlobalPrefix(String prefix) {
    System.out.println("global prefix: " + prefix);
    if (prefix.equals("/")) {
      return this;
    }
    if (prefix.endsWith("/")) {
      this.globalPrefix = prefix.substring(0, this.globalPrefix.length() - 1);
    } else {
      this.globalPrefix = prefix;
    }
    if (!this.globalPrefix.startsWith("/")) {
      this.globalPrefix = "/" + this.globalPrefix;
    }

    return this;
  }

  @Override
  public IHTTPServer getHTTPServer() {
    return this.httpAdapter;
  }
  @Override
  public TServerAdapter getHTTPAdapter() {
    return this.httpAdapter;
  }

  @Override
  public INestApplication<TServerAdapter> useGlobalInterceptors(INestInterceptor... interceptors) {
    this.globalInterceptors = interceptors;
    return this;
  }

  @Override
  public void close() {
    this.httpAdapter.close();
  }
}

